# -*- coding: utf-8 -*-
# @author : wanglei
# @date : 2020/3/31 11:57
# @description : 正态分布和自然常数e
from manimlib.imports import *
from from_victor.common_scenes.custom_scenes import *
from from_victor.common_scenes.pluggable_scenes import *
from from_victor.common_widgets.custom_widgets import *
import numpy as np



class GaussianDistribution(GraphScene):
    CONFIG = {
        "x_min": 0,
        "x_max": 10,  # X坐标范围
        "x_tick_frequency": 1,  # X坐标刻度密度
        "x_labeled_nums": range(0, 11, 1),  # X坐标刻度数值
        "x_axis_label": "$x$",  # X坐标标注

        "y_min": 0,
        "y_max": 1,  # Y坐标范围
        "y_tick_frequency": 1,  # Y坐标刻度密度
        "y_labeled_nums": range(0, 2, 1),  # Y坐标刻度数值
        "y_axis_label": "$y$",  # Y坐标标注
        "exclude_zero_label": True,  # 不显示Y坐标的0刻度

        "y_axis_height": 4,  # 坐标轴高度(屏幕高被分为 8 份)
        "x_axis_width": 7,  # 坐标轴宽度(屏幕宽被分为 14 份)
        "graph_origin": LEFT_SIDE / 2 + BOTTOM / 2,
        # 原点的位置（ORIGIN - 屏幕中心，TOP、BOTTOM、LEFT_SIDE、RIGHT_SIDE 分别指上下左右侧
    }

    def construct(self):
        # 0、logo
        # title = '正态分布'
        # self.victor_logo(title)
        # 1、正态分布定义
        # self.summary()
        # 2、正态分布公式
        self.formula()
        # 3、用山东省2013年高考理科成绩分数段与人数来说明符合正态分布

        # 4、画出新冠状病毒确诊病例符合正态分布

        # 5、音乐一般也属于正态分布

        # 6、说出下次会介绍正态分布的应用

    def func(self, x):
        mu = 5
        sigma = 0.6
        return np.exp(-((x - mu)**2)/(2*sigma**2)) / (sigma * np.sqrt(2*np.pi))

    def victor_logo(self, title):
        # e的logo
        logo = SVGMobject(
            file_name='/Users/a1/PycharmProjects/manim/assets/svg_images/e.svg',
            width = 6,
            height = 5
        )
        logo.center()
        self.play(VFadeIn(logo))

        # 添加介绍文字
        # introduce = TextMobject(
        #     "欢迎来到e知识,本次来聊" + title,
        #     tex_to_color_map={"e": RED, title: RED}
        # )
        # introduce.scale(0.7)
        # introduce.move_to(1.2 * DOWN)

    def victor_logo_old(self, title):
        """基于自然常数e的创意logo"""
        # 自然常数e
        e = TextMobject(
            "e",
            tex_to_color_map={"e": DARK_BLUE}
        )
        e.move_to(0.7 * UP + 0.85 * LEFT)
        e.scale(7)

        # 眼睛圆形
        c1_1 = Circle(arc_center=UP + LEFT, radius=3, color=WHITE, fill_color=WHITE, fill_opacity=1, stroke_width=1)
        c2_1 = Circle(arc_center=1.5 * UP + RIGHT, radius=1, color=BLACK, fill_color=BLACK, fill_opacity=1,
                    stroke_width=1)
        c3_1 = Circle(arc_center=2 * UP + RIGHT, radius=0.3, color=WHITE, fill_color=WHITE, fill_opacity=1,
                    stroke_width=1)
        c1_2 = Circle(arc_center=UP + LEFT, radius=3, color=WHITE, fill_color=WHITE, fill_opacity=1, stroke_width=1)
        c2_2 = Circle(arc_center=1.5 * UP + RIGHT, radius=1, color=BLACK, fill_color=BLACK, fill_opacity=1,
                    stroke_width=1)
        c3_2 = Circle(arc_center=2 * UP + RIGHT, radius=0.3, color=WHITE, fill_color=WHITE, fill_opacity=1,
                    stroke_width=1)

        eye_left = Group(c1_1,c2_1,c3_1)
        eye_left.scale(0.05)
        eye_right = Group(c1_2, c2_2, c3_2)
        eye_right.move_to(UP+0.6*LEFT)
        eye_right.scale(0.05)

        # 嘴巴圆形和弧线，白色填充
        mouth1 = Circle(arc_center=1.5 * UP + RIGHT, radius=1, color=WHITE, fill_color=WHITE, fill_opacity=1,stroke_width=1)
        mouth2 = ArcBetweenPoints(start=RIGHT+DOWN,end=LEFT+DOWN,angle=-np.pi/3,color=WHITE,stroke_width=3)

        # mouth1.move_to(0.5 * UP + 0.7 * LEFT)
        # mouth1.scale(0.1)

        # self.play(FadeIn(mouth1))
        # self.play(FadeIn(mouth2))

        # 移动到中间
        logo = Group(eye_left, eye_right, e)
        logo.center()

        # 添加介绍文字
        introduce = TextMobject(
            "欢迎来到e知识,本次来聊"+title,
            tex_to_color_map={"e": RED, title: RED}
        )
        introduce.scale(0.7)
        introduce.move_to(1.2*DOWN)

        # 显示
        self.play(Write(e))
        self.play(FadeInFromDown(eye_left, run_time=0.1))
        self.play(FadeInFromDown(eye_right, run_time=0.1))
        self.play(Flash(eye_left, rate_func=squish_rate_func(smooth, 0.1, 0.6)))
        self.play(Write(introduce))

        # 缩小并固化到右上角
        logo_intro = Group(logo, introduce)
        logo_intro2 = logo_intro.copy()
        logo_intro2.scale(0.3)
        logo_intro2.move_to(6*RIGHT+4*UP)
        self.play(Transform(logo_intro, logo_intro2), run_time=2)



    def summary(self):
        text1 = TextMobject(
            "当一个变量在其他因素作用下，整体会呈现正态分布;",
            tex_to_color_map={"正态分布": RED}
        )
        text2 = TextMobject(
            "就像上帝之手一样，决定了这一切！",
            tex_to_color_map={"上帝之手": RED}
        )
        group = VGroup(text1, text2)
        group.arrange(DOWN)
        group.set_width(FRAME_WIDTH - 4 * LARGE_BUFF)
        self.play(
            Write(text1)
        )
        self.play(
            Write(text2)
        )
        self.wait(1)
        self.play(
            FadeOut(text1),
            FadeOut(text2),
        )

    def formula(self):
        self.setup_axes(animate=True)
        # 定义一个红色的自定义函数 func, 例如 def func(self, x): return x
        graph = self.get_graph(self.func, color=RED)
        # 给 graph 函数图像，增加函数标签，位于 x = 8 处，位于上方
        graph_label = self.get_graph_label(graph,
                                           label="\\frac{1}{\\sqrt{2\\pi}\\sigma}e\\frac{-(x-\\mu)^2}{2\\sigma^2}",
                                           x_val=8, direction=5 * UP)
        graph_label.set_color_by_gradient(BLUE)

        # 同时绘制这两个图像
        self.play(ShowCreation(graph), ShowCreation(graph_label), run_time=3)

        axes1 = Group(*self.mobjects)
        self.play(ScaleInPlace(axes1, 0.6))  # 缩小
        line1 = Line(start=ORIGIN, end=2.5 * UP + 5 * LEFT)
        self.play(MoveAlongPath(axes1, line1), rate_func=linear)  # 将缩小后的图像移动到左上角

"""
标题和目录
"""
class TitleAndCatalog(TitleAndCatalogScene):
    CONFIG = {
        'embellish': '神奇的法则',
        'title': '正态分布',
        'catalogs': ['正态分布的由来', '正态分布的庐山真面目', '中心极限定理']
    }

class Origin(LogoScene):
    CONFIG = {
        'main_title': '神奇的法则：正态分布',
        'sub_title': '正态分布的由来'
    }
    def construct(self):
        # 1、尺子量身高，产生误差
        say(self, UP, '假设我们想知道你的真实身高')
        say(self, UP, '那么按照常识我们可以测量多次，取均值，这样最准确')
        start_mbs = self.get_mobjects()

        man = SVGMobject('man.svg').set_fill(WHITE).set_stroke(color=WHITE, width=1)
        ruler = SVGMobject('ruler.svg').set_fill(WHITE).set_stroke(color=WHITE, width=1)
        man.scale(0.5)
        man.move_to(0.35 * DOWN)
        ruler.scale(0.6)
        ruler.rotate(-PI/4)
        ruler.move_to(0.7 * LEFT)
        self.play(Write(man))
        self.play(Write(ruler))
        # 循环测量10次
        # 随机测量数据
        measure_data = []
        text_gp = Group()
        for i in range(10):
            self.measure()
            height = round(random.uniform(180, 183), 2)
            measure_data.append(height)
            text = TextMobject(str(height))
            text.scale(0.7)
            row = i//2
            column = i%2
            text.move_to((0.5 * row * DOWN + 2 * UP) + (1.5 * column * RIGHT + 2 * RIGHT))
            text_gp.add(text)
            self.play(Write(text), run_time = 0.2)
        self.play(ShowCreationThenFadeAround(text_gp, rect_animation = ShowCreation))
        # 导出箭头
        arrow = Arrow(start=3 * RIGHT, end=3 * RIGHT + 1.5 * DOWN, color=YELLOW, stroke_width=10)
        arrow_text = TextMobject('平均值', tex_to_color_map={'平均值': RED})
        arrow_text.scale(0.7)
        arrow_text.next_to(arrow)
        arrow_gp = Group(arrow, arrow_text)
        # 求均值
        sum = 0
        for v in measure_data:
            sum += v
        avg = round(sum / 10, 2)
        avg_text = TextMobject(str(avg), tex_to_color_map={str(avg): RED})
        avg_text.move_to(3 * RIGHT + 1.5 * DOWN)
        self.play(FadeIn(arrow_gp))
        self.play(Write(avg_text))
        middle_mbs = self.get_mobjects()[len(start_mbs):]
        middle_gp = Group(*middle_mbs)
        line_path = Line(start = ORIGIN, end = 4 * LEFT)
        self.play(MoveAlongPath(middle_gp, line_path), rate_func = linear)
        say(self, UP + 2 * RIGHT, '平均值真的最接近真实值吗？')
        say(self, UP + 2 * RIGHT, '或者说真实值是平均值的可能性最大')
        say(self, UP + 2 * RIGHT, '我们如何证明呢?')

    def measure(self):
        '''
        测量线
        :return:
        '''
        line = Line(color=RED, stroke_width=2)
        line.move_to(0.15 * UP)
        self.play(ShowCreationThenFadeOut(line), run_time = 0.4)

class ProbabilityDensityFunction(LogoScene):
    CONFIG = {
        'main_title': '神奇的法则：正态分布',
        'sub_title': '正态分布的由来'
    }
    def construct(self):
        # 1、误差出现的概率
        say(self, UP, '能够“观察”到这组随机误差的概率')
        p_func = TexMobject('P(\\hat{e}_{1}, \\hat{e}_{2},..., \\hat{e}_{n}) = '
                   'P(X_{1}-\\hat{\\mu}, X_{2}-\\hat{\\mu},...,X_{n}-\\hat{\\mu})')
        self.play(Write(p_func))
        # 解释变量
        line = Line(start=ORIGIN, end=0.7 * UP, color=BLUE, stroke_width=2)
        line.move_to(0.75 * RIGHT + 0.7 * UP)
        line1 = line.copy()
        line1.move_to(2.5 * LEFT + 0.7 * UP)
        line2 = line.copy()
        line2.move_to(4 * RIGHT + 0.7 * UP)
        e = TextMobject('误差的估计值',tex_to_color_map={'误差的估计值':BLUE})
        e.scale(0.7)
        e.next_to(line1, UP)
        x = TextMobject('真实高度的估计值', tex_to_color_map={'真实高度的估计值': BLUE})
        x.scale(0.7)
        x.next_to(line, UP)
        u = TextMobject('测量值',tex_to_color_map={'测量值':BLUE})
        u.scale(0.7)
        u.next_to(line2, UP)
        self.play(ShowCreation(line), ShowCreation(line1), ShowCreation(line2))
        self.play(Write(e), Write(x), Write(u))
        p_func_gp = Group(p_func, line, line1, line2, e, x, u)
        say(self, DOWN, '只要这组随机误差出现的概率最大，估计的真实值就是最接近真实值的值')
        p_func2 = TexMobject('P(\\hat{e}_{1}, \\hat{e}_{2},..., \\hat{e}_{n}) =')
        value = TexMobject('P(\\hat{e})')
        value.next_to(p_func2, RIGHT)
        p_func2_value = Group(p_func2, value)
        self.play(Transform(p_func_gp, p_func2_value))
        say(self, DOWN, '由此只研究单个随机误差取不同数值的可能性就可以了')
        self.play(ShowCreationThenFadeAround(value, rect_animation = ShowCreation))
        #
        # # 2、概率密度函数概念
        # say(self, UP, '首先我们要引入一个概念，概率密度函数')
        # pdf_text1 = TextMobject('连续型随机变量的概率密度函数:',
        #                         tex_to_color_map={'连续型随机变量': BLUE,
        #                                          '概率密度函数': RED,
        #                                          })
        # pdf_text1.scale(0.7)
        # pdf_text1.move_to(UP)
        # pdf_text2 = TextMobject('是一个描述这个随机变量的输出值,在某个确定的取值点附近的可能性函数',
        #                         tex_to_color_map={'可能性函数': BLUE})
        # pdf_text2.scale(0.7)
        # self.play(Write(pdf_text1))
        # self.play(Write(pdf_text2))
        # # 3、带有这种性质的概率密度函数有很多
        # say(self, UP, '如果我们知道随机误差的概率密度函数是什么')
        # say(self, UP, '那么就意味着可以求出最可能的误差值，也就可以得出最可能的预测值了')









class Test(Scene):
    def construct(self):
        # text = TextMobject('求出最可能的误差值 separated',tex_to_color_map={"求出最可能的误差值 separated": BLACK})
        # text.scale(0.7)
        # text.move_to(2*UP)
        # self.play(Write(text))
        # text2 = Text('求出最可能的误差值 separated', color=BLACK, font='PingFangSC-Regular')
        # text2.scale(0.3)
        # self.play(Write(text2))
        # self.wait(1)
        logo_gp = Logo_gp()
        logo_gp.scale(0.7)
        logo_gp.move_to(3 * LEFT + 6 * UP)
        self.play(FadeInFromLarge(logo_gp), run_time = 2)